import sys
import time
import pyads
import traceback
from loguru import logger


class AdsComm():
    def __init__(self, amsNetID, varName, terminalDisplay, varType):
        self.adsConnectState = None
        self.amsNetID = amsNetID
        self.varName = varName
        self.terminalDisplay = terminalDisplay.upper()  # 小写字母转换为大写字母
        self.varType = AdsComm.getValueType(self, varType.upper())
        self.varSize = AdsComm.getValueSize(self, varType.upper())

        try:
            self.plc = pyads.Connection(self.amsNetID, pyads.PORT_TC3PLC1)
        except:
            logger.error('连接 ADS 失败. 请检查 AmsNetID')
            traceback.print_exc()
            input('按下任意键退出程序')
            sys.exit()

        # 存储日志配置
        logger.add('Read variable record.log', rotation="1MB")

    # 开启 ADS 连接
    def adsOpen(self):
        try:
            self.plc.open()
            logger.info('通信状态:{}   AmsNetID:{}', AdsComm.adsState(self), self.amsNetID)
        except:
            logger.error('打开 ADS 失败. 请检查 AmsNetID 或者目标控制器状态')
            traceback.print_exc()
            input('按下任意键退出程序')
            sys.exit()
        if self.adsConnectState[0] == 5:
            if self.terminalDisplay == 'Y': # 注册回调，变量发生变化将自动获取
                AdsComm.callbackRegistration(self, self.varName, self.varType, self.varSize)
            else:
                var_Value = self.plc.read_by_name(self.varName, self.varType)
                logger.info(var_Value)
            while True:
                time.sleep(1)
                if self.terminalDisplay == 'N':
                    get_value = input('再次读取 Y/N:')
                    if get_value.upper() == 'Y':
                        var_Value = self.plc.read_by_name(self.varName, self.varType)
                        logger.info(var_Value)
                    else:
                        sys.exit()
        else:
            logger.error('通信建立失败')

    # 关闭 ADS 连接
    def adsClose(self):
        self.plc.close()
        logger.info('adsConnectState:{}'  'AmsNetID:{}', AdsComm.adsState(self), self.amsNetID)

    # 获取 ADS 连接状态
    def adsState(self):
        self.adsConnectState = self.plc.read_state()
        if self.adsConnectState[0] == 5:
            return '建立成功'
        else:
            return '建立失败'

    def getValueType(self, intype):
        match intype:
            case 'BOOL':
                return pyads.PLCTYPE_BOOL
            case 'BYTE':
                return pyads.PLCTYPE_BYTE
            case 'DWORD':
                return pyads.PLCTYPE_DWORD
            case 'DINT':
                return pyads.PLCTYPE_DINT
            case 'INT':
                return pyads.PLCTYPE_INT
            case 'LREAL':
                return pyads.PLCTYPE_LREAL
            case 'REAL':
                return pyads.PLCTYPE_REAL
            case 'SINT':
                return pyads.PLCTYPE_SINT
            case 'STRING':
                return pyads.PLCTYPE_STRING
            case 'WSTRING':
                return pyads.PLCTYPE_WSTRING
            case 'TOD':
                return pyads.PLCTYPE_TOD
            case 'UBYTE':
                return pyads.PLCTYPE_UBYTE
            case 'UDINT':
                return pyads.PLCTYPE_UDINT
            case 'UINT':
                return pyads.PLCTYPE_UINT
            case 'USINT':
                return pyads.PLCTYPE_UINT
            case 'WORD':
                return pyads.PLCTYPE_WORD
            case 'LINT':
                return pyads.PLCTYPE_LINT
            case 'ULINT':
                return pyads.PLCTYPE_ULINT
            case 'DATE':
                return pyads.PLCTYPE_DATE
            case 'DATE_AND_TIME':
                return pyads.PLCTYPE_DATE_AND_TIME
            case 'DT':
                return pyads.ATE_AND_TIME
            case 'TIME':
                return pyads.PLCTYPE_TIME

    def getValueSize(self, intype):
        match intype:
            case 'BOOL': return 1
            case 'BYTE': return 1
            case 'DWORD': return 4
            case 'DINT': return 4
            case 'INT': return 2
            case 'LREAL': return 8
            case 'REAL': return 4
            case 'SINT': return 1
            case 'STRING':
                string_size = input('请输入 STRING 类型变量长度<byte>:')
                return int(string_size)
            case 'WSTRING':
                wstring_size = input('请输入 WSTRING 类型变量长度<byte>:')
                return int(wstring_size)
            case 'TOD': return 4
            case 'UBYTE': return 1
            case 'UDINT': return 4
            case 'UINT': return 2
            case 'USINT': return 2
            case 'WORD': return 2
            case 'LINT': return 8
            case 'ULINT': return 8
            case 'DATE': return 4
            case 'DATE_AND_TIME': return 4
            case 'DT': return 4
            case 'TIME': return 4

    # 注册 ADS 通信回调函数,变量名称
    def callbackRegistration(self, plcVariable, varType, varLength):
        logger.info('name: {}  type: {}  size: {}', plcVariable, varType, varLength)
        try:
            @self.plc.notification(varType)
            def callback(handle, name, timestamp, value):
                logger.info(value)
            self.plc.add_device_notification(plcVariable, pyads.NotificationAttrib(varLength), callback)
        except:
            logger.error('ADS 变量注册回调失败，请检查输入变量名称')
            traceback.print_exc()
            input('按下任意键退出程序')
            sys.exit()



if __name__ == "__main__":
    ams_net_id = input('请输入目标控制器 AmsNetID:')
    var_name = input('请输入变量名称:')
    var_type = input('请输入变量类型:')
    terminal_display = input('自动获取该变量值并在终端打印 Y/N :')
    ads = AdsComm(ams_net_id, var_name, terminal_display, var_type)
    ads.adsOpen()
